/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */
#pragma once

#include <textstream/TextStream.h>
#include <threads/Lock.h>
#include <string>

namespace log4cplus {
	class Logger;
}

namespace liba {
namespace logs {

enum LogLevel
{
	LOG_LEVEL_TRACE = 0,
	LOG_LEVEL_DEBUG = 10000,
	LOG_LEVEL_INFO = 20000,
	LOG_LEVEL_WARNING = 30000,
	LOG_LEVEL_ERROR = 40000,
	LOG_LEVEL_FATAL = 50000
};

bool IsLogLevelEnabled(const char* loggerName, LogLevel level);
void DoLog(const char* loggerName, LogLevel level, const char* file, int line, const char* format, ...);

#define LOG_TRACE(loggerName, format,...) \
	do { \
        if (::liba::logs::IsLogLevelEnabled(loggerName, ::liba::logs::LOG_LEVEL_TRACE)) \
        { \
            ::liba::logs::DoLog(loggerName, ::liba::logs::LOG_LEVEL_TRACE, __FILE__, __LINE__, format,##__VA_ARGS__); \
        } \
	} while (0)
	
#define LOG_DEBUG(loggerName, format,...) \
	do { \
        if (::liba::logs::IsLogLevelEnabled(loggerName, ::liba::logs::LOG_LEVEL_DEBUG)) \
        { \
            ::liba::logs::DoLog(loggerName, ::liba::logs::LOG_LEVEL_DEBUG, __FILE__, __LINE__, format,##__VA_ARGS__); \
        } \
	} while (0)

#define LOG_INFO(loggerName, format,...) \
	do { \
        if (::liba::logs::IsLogLevelEnabled(loggerName, ::liba::logs::LOG_LEVEL_INFO)) \
        { \
            ::liba::logs::DoLog(loggerName, ::liba::logs::LOG_LEVEL_INFO, __FILE__, __LINE__, format,##__VA_ARGS__); \
        } \
	} while (0)

#define LOG_WARN(loggerName, format,...) \
	do { \
        if (::liba::logs::IsLogLevelEnabled(loggerName, ::liba::logs::LOG_LEVEL_WARNING)) \
        { \
            ::liba::logs::DoLog(loggerName, ::liba::logs::LOG_LEVEL_WARNING, __FILE__, __LINE__, format,##__VA_ARGS__); \
        } \
	} while (0)

#define LOG_ERROR(loggerName, format,...) \
	do { \
        if (::liba::logs::IsLogLevelEnabled(loggerName, ::liba::logs::LOG_LEVEL_ERROR)) \
        { \
            ::liba::logs::DoLog(loggerName, ::liba::logs::LOG_LEVEL_ERROR, __FILE__, __LINE__, format,##__VA_ARGS__); \
        } \
	} while (0)

#define LOG_FATAL(loggerName, format,...) \
	do { \
        if (::liba::logs::IsLogLevelEnabled(loggerName, ::liba::logs::LOG_LEVEL_FATAL)) \
        { \
            ::liba::logs::DoLog(loggerName, ::liba::logs::LOG_LEVEL_FATAL, __FILE__, __LINE__, format,##__VA_ARGS__); \
        } \
	} while (0)


/** This class adds error tracking and thread-safety to {@link liba::textstream::TextOStream}. */
template<class Sym>
class Log : public textstream::TextOStream<Sym>, public threads::Mutex
{
public:
	virtual void inc_error_counter()=0;
	virtual void inc_warning_counter()=0;
};

/** Outputting the object of this type to Log increments error counter. */
class ErrorManipulator
{};
/** Outputting the object of this type to Log increments warning counter. */
class WarningManipulator
{};

const ErrorManipulator error = ErrorManipulator();
const WarningManipulator warning = WarningManipulator();

Log<char> & log();
Log<wchar_t> & wlog();

template<class Sym, class T>
inline Log<Sym> & operator<<(Log<Sym> & ts, const T & value)
{
	threads::Lock lock( ts );
	textstream::TextOStream<Sym> & tts = ts;
	tts << value;
	return ts;
}

/** Microsoft Visual Studio 6 distincts char[n] type from char * type
	and that's why this specialization is reqired to print literals. */
inline Log<char> & operator<<(Log<char> & ts, const char * value)
{
	threads::Lock lock( ts );
	textstream::TextOStream<char> & tts = ts;
	tts << value;
	return ts;
}

/** Microsoft Visual Studio 6 distincts char[n] type from char * type
	and that's why this specialization is reqired to print literals. */
inline Log<char> & operator<<(Log<char> & ts, char * value)
{
	threads::Lock lock( ts );
	textstream::TextOStream<char> & tts = ts;
	tts << value;
	return ts;
}

/** Microsoft Visual Studio 6 distincts wchar_t[n] type from wchar_t * type
	and that's why this specialization is reqired to print literals. */
inline Log<wchar_t> & operator<<(Log<wchar_t> & ts, const wchar_t * value)
{
	threads::Lock lock( ts );
	textstream::TextOStream<wchar_t> & tts = ts;
	tts << value;
	return ts;
}

/** Microsoft Visual Studio 6 distincts wchar_t[n] type from wchar_t * type
	and that's why this specialization is reqired to print literals. */
inline Log<wchar_t> & operator<<(Log<wchar_t> & ts, wchar_t * value)
{
	threads::Lock lock( ts );
	textstream::TextOStream<wchar_t> & tts = ts;
	tts << value;
	return ts;
}


/** Microsoft Visual Studio 6 does not support partial specialization,
	that's why this full specialization is required. */
inline Log<char> & operator<<(Log<char> & ts, const ErrorManipulator & value)
{
	threads::Lock lock( ts );
	ts.inc_error_counter();
	return ts;
}

/** Microsoft Visual Studio 6 does not support partial specialization,
	that's why this full specialization is required. */
inline Log<wchar_t> & operator<<(Log<wchar_t> & ts, const ErrorManipulator & value)
{
	threads::Lock lock( ts );
	ts.inc_error_counter();
	return ts;
}

/** Microsoft Visual Studio 6 does not support partial specialization,
	that's why this full specialization is required. */
inline Log<char> & operator<<(Log<char> & ts, const WarningManipulator & value)
{
	threads::Lock lock( ts );
	ts.inc_warning_counter();
	return ts;
}

/** Microsoft Visual Studio 6 does not support partial specialization,
	that's why this full specialization is required. */
inline Log<wchar_t> & operator<<(Log<wchar_t> & ts, const WarningManipulator & value)
{
	threads::Lock lock( ts );
	ts.inc_warning_counter();
	return ts;
}

class Creator
{
public:
	Creator(const std::string & app_name = "");
	Creator(Log<char> * l);
	Creator(Log<wchar_t> * wl);
	~Creator();
};


} // namespace logs
} // namespace liba

using namespace liba;
